package com.whcoding.test.common;

import com.jcraft.jsch.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;

/**
 * @program: spring-boot-learning
 * @description:
 * @author: whcoding
 * @create: 2022-04-28 11:37
 **/
public class SFTPUtils {
	private static final Logger log = LoggerFactory.getLogger(SFTPUtils.class);

	private String host; // 主机名称/IP
	private int port = 22; // 端口
	private String username; // 用户名
	private String password; // 密码

	private ChannelSftp sftp = null;
	private Channel channel = null;
	private Session session = null;

	public SFTPUtils(String host, String userName, String password) {
		this.host = host;
		this.username = userName;
		this.password = password;
	}

	public SFTPUtils(String host, int port, String userName, String password) {
		this.host = host;
		this.port = port;
		this.username = userName;
		this.password = password;
	}

	/**
	 * 连接SFTP服务器
	 *
	 * @throws JSchException
	 */
	public void connect() throws JSchException {
		JSch jSch = new JSch();
		session = jSch.getSession(username, host, port);
		session.setPassword(password);
		session.setConfig(this.buildConfig());
		session.connect();
		channel = session.openChannel("sftp");
		channel.connect();
		sftp = (ChannelSftp) channel;
		log.info("连接主机：{} 登录成功", host);
	}

	/**
	 * 构建连接配置参数
	 *
	 * @return Properties
	 */
	private Properties buildConfig() {
		Properties properties = new Properties();
		properties.put("StrictHostKeyChecking", "no");
		return properties;
	}

	/**
	 * 关闭连接
	 */
	public void disconnect() {
		try {
			if (sftp.isConnected()) {
				sftp.disconnect();
			}
			if (channel.isConnected()) {
				channel.disconnect();
			}
			if (session.isConnected()) {
				session.disconnect();
			}
		} catch (Throwable e) {
			//ignore
		}
	}

	/**
	 * 下载文件
	 *
	 * @param sftpPath   服务器路径，不指定路径默认是FTP的根路径，指定路径是指的SFTP的根路径下开始。
	 *                   例如：SFTP根路径为：/sftp/file，那么默认下载文件会去根路径下载，而指定 sftpPath 也是从根路径下开始；
	 *                   指定 sftpPath 为 word，那么是从 /sftp/file/word 路径中查找文件下载。为空表示忽略该参数。
	 * @param fileName   文件名
	 * @param toFilePath 下载保存文件路径，路径+文件名，例如：d:/test.txt
	 * @return
	 */
	public boolean downloadFile(String sftpPath, String fileName, String toFilePath) {
		FileOutputStream fileOutputStream = null;
		try {
			if (StringUtils.isNotBlank(sftpPath)) {
				sftp.cd(sftpPath);
			}
			fileOutputStream = new FileOutputStream(new File(toFilePath));
			sftp.get(fileName, fileOutputStream);
			return true;
		} catch (Exception e) {
			log.error("下载文件错误", e);
		} finally {
			if (fileOutputStream != null) {
				try {
					fileOutputStream.close();
				} catch (IOException e) {
					//ignore
				}
			}
		}
		return false;
	}

	/**
	 * 上传文件
	 *
	 * @param sftpPath      服务器路径，不指定路径默认是FTP的根路径，指定路径是指的SFTP的根路径下开始。
	 *                      例如：SFTP根路径为：/sftp/file，那么默认下载文件会去根路径下载，而指定 sftpPath 也是从根路径下开始；
	 *                      指定 sftpPath 为 word，那么是从 /sftp/file/word 路径中查找文件下载。为空表示忽略该参数。
	 * @param fileName      上传后文件名
	 * @param localFilePath 文件路径，路径+文件名，例如：d:/test.txt
	 * @return
	 */
	public boolean uploadFile(String sftpPath, String fileName, String localFilePath) {
		FileInputStream inputStream = null;
		try {
			if (StringUtils.isNotBlank(sftpPath)) {
				sftp.cd(sftpPath);
			}
			inputStream = new FileInputStream(new File(localFilePath));
			sftp.put(inputStream, fileName);
			return true;
		} catch (Exception e) {
			log.error("上传文件错误", e);
		} finally {
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					//ignore
				}
			}
		}
		return false;
	}

	/**
	 * 上传文件
	 *
	 * @param sftpPath    服务器路径，不指定路径默认是FTP的根路径，指定路径是指的SFTP的根路径下开始。
	 *                    例如：SFTP根路径为：/sftp/file，那么默认下载文件会去根路径下载，而指定 sftpPath 也是从根路径下开始；
	 *                    指定 sftpPath 为 word，那么是从 /sftp/file/word 路径中查找文件下载。为空表示忽略该参数。
	 * @param fileName    上传后文件名
	 * @param inputStream 文件输入流
	 * @return
	 */
	public boolean uploadFile(String sftpPath, String fileName, InputStream inputStream) {
		try {
			if (StringUtils.isNotBlank(sftpPath)) {
				sftp.cd(sftpPath);
			}
			sftp.put(inputStream, fileName);
			return true;
		} catch (Exception e) {
			log.error("上传文件错误", e);
		} finally {
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					//ignore
				}
			}
		}
		return false;
	}

	/**
	 * 删除文件
	 *
	 * @param sftpPath 服务器路径，不指定路径默认是FTP的根路径，指定路径是指的SFTP的根路径下开始。
	 *                 例如：SFTP根路径为：/sftp/file，那么默认下载文件会去根路径下载，而指定 sftpPath 也是从根路径下开始；
	 *                 指定 sftpPath 为 word，那么是从 /sftp/file/word 路径中查找文件下载。为空表示忽略该参数。
	 * @param fileName 文件名
	 * @return
	 */
	public boolean deleteFile(String sftpPath, String fileName) {
		try {
			if (StringUtils.isNotBlank(sftpPath)) {
				sftp.cd(sftpPath);
			}
			sftp.rm(fileName);
			return true;
		} catch (Exception e) {
			log.error("删除文件失败", e);
		}
		return false;
	}

	/**
	 * 查询指定目录下信息
	 *
	 * @param sftpPath 服务器路径，不指定路径默认是FTP的根路径，指定路径是指的SFTP的根路径下开始。
	 *                 例如：SFTP根路径为：/sftp/file，那么默认下载文件会去根路径下载，而指定 sftpPath 也是从根路径下开始；
	 *                 指定 sftpPath 为 word，那么是从 /sftp/file/word 路径中查找文件下载。为空表示忽略该参数。
	 * @return
	 */
	public List<String> listFiles(String sftpPath) throws SftpException {
		Vector files = sftp.ls(sftpPath);
		List<String> result = new ArrayList<String>();
		Iterator iterator = files.iterator();
		while (iterator.hasNext()) {
			ChannelSftp.LsEntry isEntity = (ChannelSftp.LsEntry) iterator.next();
			result.add(isEntity.getFilename());
		}
		return result;
	}


	public static void main(String[] args) throws IOException, JSchException {
		long start = System.currentTimeMillis();

		SFTPUtils sftp = new SFTPUtils("172.16.57.156", 22, "root", "1q2w3e");
		sftp.connect();
		System.out.println("连接成功");
		File file = new File("E:\\temp\\temp_cent_test.iso");
		InputStream is = new FileInputStream(file);
		sftp.uploadFile("/vincent", "temp_centOS.iso", is);
		sftp.disconnect();
		long finish = System.currentTimeMillis();
		long timeElapsed = finish - start;
		System.out.println("上传文件耗时" + timeElapsed / 1000 + "秒");
	}
}
